#![allow(unused)]
use alloc::vec::Vec;
use embedded_dma::ReadBuffer;

use crate::{alloc_coherent, dealloc_coherent, BusAddr, DMAInfo};
use core::{
    alloc::Layout,
    marker::PhantomData,
    mem::size_of,
    ptr::{self, slice_from_raw_parts_mut},
};

pub struct ArrayCoherent<T> {
    info: DMAInfo,
    layout: Layout,
    _marker: PhantomData<T>,
}
impl<T: Sized> ArrayCoherent<T> {
    /// Constructs a [ArrayCoherent] from a given `len` and `align`,
    /// or panic if any of the following conditions
    /// are not met:
    ///
    /// * `align` must not be zero,
    ///
    /// * `align` must be a power of two,
    ///
    /// * `size`, when rounded up to the nearest multiple of `align`,
    ///    must not overflow isize (i.e., the rounded value must be
    ///    less than or equal to `isize::MAX`).
    pub fn zero(len: usize, align: usize) -> Option<Self> {
        unsafe {
            let size = size_of::<T>() * len;
            let layout = Layout::from_size_align(size, align).unwrap();
            let info = alloc_coherent(layout)?;
            let list = &mut *slice_from_raw_parts_mut(info.cpu_addr as *mut u8, size);
            list.fill(0);

            Some(Self {
                info,
                layout,
                _marker: PhantomData,
            })
        }
    }

    pub fn len(&self) -> usize {
        self.layout.size() / size_of::<T>()
    }

    #[inline(always)]
    pub fn as_ptr(&self) -> *const T {
        self.info.cpu_addr as *const T
    }
    #[inline(always)]
    pub fn as_mut_ptr(&mut self) -> *mut T {
        self.info.cpu_addr as *mut T
    }

    pub fn bus_addr(&self) -> BusAddr {
        BusAddr::new(self.info.bus_addr)
    }

    pub fn read_volatile(&self) -> Vec<T> {
        let mut out = Vec::with_capacity(self.len());
        unsafe {
            let ptr = self.as_ptr();
            for i in 0..self.len() {
                out.push(ptr.offset(i as isize).read_volatile());
            }
        }
        out
    }

    pub fn read_volatile_at(&self, index: usize) -> T {
        unsafe {
            let ptr = self.as_ptr();
            ptr.offset(index as isize).read_volatile()
        }
    }

    pub fn write_volatile<V>(&mut self, val: V)
    where
        V: IntoIterator<Item = T>,
    {
        unsafe {
            let ptr = self.as_mut_ptr();
            for (i, one) in val.into_iter().enumerate() {
                ptr.offset(i as isize).write_volatile(one);
            }
        }
    }

    pub fn write_volatile_at(&mut self, val: T, index: usize) {
        unsafe {
            let ptr = self.as_mut_ptr();
            ptr.offset(index as isize).write_volatile(val);
        }
    }
}

impl<T> Drop for ArrayCoherent<T> {
    fn drop(&mut self) {
        unsafe {
            let list = &mut *slice_from_raw_parts_mut(self.info.cpu_addr as *mut T, self.len());
            for one in list {
                ptr::drop_in_place(one as *mut T);
            }
            dealloc_coherent(self.info, self.layout);
        }
    }
}
